package cn.iocoder.yudao.module.system.service.teacherdashboard;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.module.system.controller.admin.teacherdashboard.vo.TeacherDashboardRespVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 教师看板缓存服务
 * 提供多层级缓存策略，提升查询性能
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class TeacherDashboardCacheService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // ==================== 缓存键前缀定义 ====================
    
    private static final String CACHE_PREFIX = "teacher:dashboard:";
    
    // L1缓存：考试基础统计数据（30分钟）
    private static final String EXAM_STATS_KEY = CACHE_PREFIX + "exam:stats:";
    private static final Duration EXAM_STATS_TTL = Duration.ofMinutes(30);
    
    // L2缓存：班级排名数据（1小时）
    private static final String CLASS_RANK_KEY = CACHE_PREFIX + "class:rank:";
    private static final Duration CLASS_RANK_TTL = Duration.ofHours(1);
    
    // L3缓存：年级统计数据（2小时）
    private static final String GRADE_STATS_KEY = CACHE_PREFIX + "grade:stats:";
    private static final Duration GRADE_STATS_TTL = Duration.ofHours(2);
    
    // 学生排名缓存（45分钟）
    private static final String STUDENT_RANKING_KEY = CACHE_PREFIX + "student:ranking:";
    private static final Duration STUDENT_RANKING_TTL = Duration.ofMinutes(45);
    
    // 科目统计缓存（1小时）
    private static final String SUBJECT_STATS_KEY = CACHE_PREFIX + "subject:stats:";
    private static final Duration SUBJECT_STATS_TTL = Duration.ofHours(1);
    
    // 对比分析缓存（2小时）
    private static final String COMPARISON_KEY = CACHE_PREFIX + "comparison:";
    private static final Duration COMPARISON_TTL = Duration.ofHours(2);
    
    // 完整看板数据缓存（15分钟）
    private static final String DASHBOARD_KEY = CACHE_PREFIX + "full:";
    private static final Duration DASHBOARD_TTL = Duration.ofMinutes(15);

    // ==================== 考试统计数据缓存 ====================

    /**
     * 获取考试统计数据（带缓存）
     */
    public Map<String, Object> getExamStatistics(Long examId) {
        String key = EXAM_STATS_KEY + examId;
        
        try {
            @SuppressWarnings("unchecked")
            Map<String, Object> stats = (Map<String, Object>) redisTemplate.opsForValue().get(key);
            
            if (stats != null) {
                log.debug("从缓存获取考试统计数据，examId: {}", examId);
                return stats;
            }
            
            log.debug("缓存未命中，考试统计数据需要从数据库查询，examId: {}", examId);
            return null;
            
        } catch (Exception e) {
            log.warn("获取考试统计缓存失败，examId: {}, error: {}", examId, e.getMessage());
            return null;
        }
    }

    /**
     * 缓存考试统计数据
     */
    public void cacheExamStatistics(Long examId, Map<String, Object> stats) {
        if (examId == null || stats == null) {
            return;
        }
        
        String key = EXAM_STATS_KEY + examId;
        
        try {
            redisTemplate.opsForValue().set(key, stats, EXAM_STATS_TTL);
            log.debug("缓存考试统计数据成功，examId: {}, ttl: {}分钟", examId, EXAM_STATS_TTL.toMinutes());
        } catch (Exception e) {
            log.warn("缓存考试统计数据失败，examId: {}, error: {}", examId, e.getMessage());
        }
    }

    // ==================== 班级排名数据缓存 ====================

    /**
     * 获取班级排名数据（带缓存）
     */
    public List<Map<String, Object>> getClassRanking(Long classId, Long examId) {
        String key = CLASS_RANK_KEY + classId + ":" + examId;
        
        try {
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> ranking = (List<Map<String, Object>>) redisTemplate.opsForValue().get(key);
            
            if (ranking != null) {
                log.debug("从缓存获取班级排名数据，classId: {}, examId: {}", classId, examId);
                return ranking;
            }
            
            log.debug("缓存未命中，班级排名数据需要从数据库查询，classId: {}, examId: {}", classId, examId);
            return null;
            
        } catch (Exception e) {
            log.warn("获取班级排名缓存失败，classId: {}, examId: {}, error: {}", classId, examId, e.getMessage());
            return null;
        }
    }

    /**
     * 缓存班级排名数据
     */
    public void cacheClassRanking(Long classId, Long examId, List<Map<String, Object>> ranking) {
        if (classId == null || examId == null || ranking == null) {
            return;
        }
        
        String key = CLASS_RANK_KEY + classId + ":" + examId;
        
        try {
            redisTemplate.opsForValue().set(key, ranking, CLASS_RANK_TTL);
            log.debug("缓存班级排名数据成功，classId: {}, examId: {}, size: {}, ttl: {}小时", 
                     classId, examId, ranking.size(), CLASS_RANK_TTL.toHours());
        } catch (Exception e) {
            log.warn("缓存班级排名数据失败，classId: {}, examId: {}, error: {}", classId, examId, e.getMessage());
        }
    }

    // ==================== 年级统计数据缓存 ====================

    /**
     * 获取年级统计数据（带缓存）
     */
    public Map<String, Object> getGradeStatistics(Long examId) {
        String key = GRADE_STATS_KEY + examId;
        
        try {
            @SuppressWarnings("unchecked")
            Map<String, Object> stats = (Map<String, Object>) redisTemplate.opsForValue().get(key);
            
            if (stats != null) {
                log.debug("从缓存获取年级统计数据，examId: {}", examId);
                return stats;
            }
            
            log.debug("缓存未命中，年级统计数据需要从数据库查询，examId: {}", examId);
            return null;
            
        } catch (Exception e) {
            log.warn("获取年级统计缓存失败，examId: {}, error: {}", examId, e.getMessage());
            return null;
        }
    }

    /**
     * 缓存年级统计数据
     */
    public void cacheGradeStatistics(Long examId, Map<String, Object> stats) {
        if (examId == null || stats == null) {
            return;
        }
        
        String key = GRADE_STATS_KEY + examId;
        
        try {
            redisTemplate.opsForValue().set(key, stats, GRADE_STATS_TTL);
            log.debug("缓存年级统计数据成功，examId: {}, ttl: {}小时", examId, GRADE_STATS_TTL.toHours());
        } catch (Exception e) {
            log.warn("缓存年级统计数据失败，examId: {}, error: {}", examId, e.getMessage());
        }
    }

    // ==================== 学生排名数据缓存 ====================

    /**
     * 获取学生排名数据（带缓存）
     */
    public List<Map<String, Object>> getStudentRanking(Long examId, Integer page, Integer size) {
        String key = STUDENT_RANKING_KEY + examId + ":" + page + ":" + size;
        
        try {
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> ranking = (List<Map<String, Object>>) redisTemplate.opsForValue().get(key);
            
            if (ranking != null) {
                log.debug("从缓存获取学生排名数据，examId: {}, page: {}, size: {}", examId, page, size);
                return ranking;
            }
            
            log.debug("缓存未命中，学生排名数据需要从数据库查询，examId: {}, page: {}, size: {}", examId, page, size);
            return null;
            
        } catch (Exception e) {
            log.warn("获取学生排名缓存失败，examId: {}, page: {}, size: {}, error: {}", examId, page, size, e.getMessage());
            return null;
        }
    }

    /**
     * 缓存学生排名数据
     */
    public void cacheStudentRanking(Long examId, Integer page, Integer size, List<Map<String, Object>> ranking) {
        if (examId == null || page == null || size == null || ranking == null) {
            return;
        }
        
        String key = STUDENT_RANKING_KEY + examId + ":" + page + ":" + size;
        
        try {
            redisTemplate.opsForValue().set(key, ranking, STUDENT_RANKING_TTL);
            log.debug("缓存学生排名数据成功，examId: {}, page: {}, size: {}, count: {}, ttl: {}分钟", 
                     examId, page, size, ranking.size(), STUDENT_RANKING_TTL.toMinutes());
        } catch (Exception e) {
            log.warn("缓存学生排名数据失败，examId: {}, page: {}, size: {}, error: {}", examId, page, size, e.getMessage());
        }
    }

    // ==================== 科目统计数据缓存 ====================

    /**
     * 获取科目统计数据（带缓存）
     */
    public List<Map<String, Object>> getSubjectStatistics(Long examId) {
        String key = SUBJECT_STATS_KEY + examId;
        
        try {
            @SuppressWarnings("unchecked")
            List<Map<String, Object>> stats = (List<Map<String, Object>>) redisTemplate.opsForValue().get(key);
            
            if (stats != null) {
                log.debug("从缓存获取科目统计数据，examId: {}", examId);
                return stats;
            }
            
            log.debug("缓存未命中，科目统计数据需要从数据库查询，examId: {}", examId);
            return null;
            
        } catch (Exception e) {
            log.warn("获取科目统计缓存失败，examId: {}, error: {}", examId, e.getMessage());
            return null;
        }
    }

    /**
     * 缓存科目统计数据
     */
    public void cacheSubjectStatistics(Long examId, List<Map<String, Object>> stats) {
        if (examId == null || stats == null) {
            return;
        }
        
        String key = SUBJECT_STATS_KEY + examId;
        
        try {
            redisTemplate.opsForValue().set(key, stats, SUBJECT_STATS_TTL);
            log.debug("缓存科目统计数据成功，examId: {}, count: {}, ttl: {}小时", 
                     examId, stats.size(), SUBJECT_STATS_TTL.toHours());
        } catch (Exception e) {
            log.warn("缓存科目统计数据失败，examId: {}, error: {}", examId, e.getMessage());
        }
    }

    // ==================== 完整看板数据缓存 ====================

    /**
     * 获取完整看板数据（带缓存）
     */
    public TeacherDashboardRespVO getFullDashboard(String cacheKey) {
        if (StrUtil.isBlank(cacheKey)) {
            return null;
        }
        
        String key = DASHBOARD_KEY + cacheKey;
        
        try {
            String jsonStr = (String) redisTemplate.opsForValue().get(key);
            
            if (StrUtil.isNotBlank(jsonStr)) {
                TeacherDashboardRespVO dashboard = JSONUtil.toBean(jsonStr, TeacherDashboardRespVO.class);
                log.debug("从缓存获取完整看板数据，cacheKey: {}", cacheKey);
                return dashboard;
            }
            
            log.debug("缓存未命中，完整看板数据需要重新构建，cacheKey: {}", cacheKey);
            return null;
            
        } catch (Exception e) {
            log.warn("获取完整看板缓存失败，cacheKey: {}, error: {}", cacheKey, e.getMessage());
            return null;
        }
    }

    /**
     * 缓存完整看板数据
     */
    public void cacheFullDashboard(String cacheKey, TeacherDashboardRespVO dashboard) {
        if (StrUtil.isBlank(cacheKey) || dashboard == null) {
            return;
        }
        
        String key = DASHBOARD_KEY + cacheKey;
        
        try {
            String jsonStr = JSONUtil.toJsonStr(dashboard);
            redisTemplate.opsForValue().set(key, jsonStr, DASHBOARD_TTL);
            log.debug("缓存完整看板数据成功，cacheKey: {}, ttl: {}分钟", cacheKey, DASHBOARD_TTL.toMinutes());
        } catch (Exception e) {
            log.warn("缓存完整看板数据失败，cacheKey: {}, error: {}", cacheKey, e.getMessage());
        }
    }

    // ==================== 缓存失效管理 ====================

    /**
     * 清除考试相关的所有缓存
     */
    public void invalidateExamCache(Long examId) {
        if (examId == null) {
            return;
        }
        
        try {
            // 清除考试统计缓存
            redisTemplate.delete(EXAM_STATS_KEY + examId);
            
            // 清除年级统计缓存
            redisTemplate.delete(GRADE_STATS_KEY + examId);
            
            // 清除科目统计缓存
            redisTemplate.delete(SUBJECT_STATS_KEY + examId);
            
            // 清除学生排名缓存（模糊匹配）
            String studentRankingPattern = STUDENT_RANKING_KEY + examId + ":*";
            redisTemplate.delete(redisTemplate.keys(studentRankingPattern));
            
            // 清除完整看板缓存（模糊匹配）
            String dashboardPattern = DASHBOARD_KEY + "*:" + examId + ":*";
            redisTemplate.delete(redisTemplate.keys(dashboardPattern));
            
            log.info("清除考试缓存成功，examId: {}", examId);
            
        } catch (Exception e) {
            log.warn("清除考试缓存失败，examId: {}, error: {}", examId, e.getMessage());
        }
    }

    /**
     * 清除班级相关的所有缓存
     */
    public void invalidateClassCache(Long classId, Long examId) {
        if (classId == null || examId == null) {
            return;
        }
        
        try {
            // 清除班级排名缓存
            redisTemplate.delete(CLASS_RANK_KEY + classId + ":" + examId);
            
            // 清除对比分析缓存
            String comparisonPattern = COMPARISON_KEY + classId + ":*";
            redisTemplate.delete(redisTemplate.keys(comparisonPattern));
            
            // 清除完整看板缓存（模糊匹配）
            String dashboardPattern = DASHBOARD_KEY + "*:" + classId + ":" + examId + ":*";
            redisTemplate.delete(redisTemplate.keys(dashboardPattern));
            
            log.info("清除班级缓存成功，classId: {}, examId: {}", classId, examId);
            
        } catch (Exception e) {
            log.warn("清除班级缓存失败，classId: {}, examId: {}, error: {}", classId, examId, e.getMessage());
        }
    }

    /**
     * 清除所有看板缓存
     */
    public void invalidateAllDashboardCache() {
        try {
            String pattern = CACHE_PREFIX + "*";
            redisTemplate.delete(redisTemplate.keys(pattern));
            log.info("清除所有看板缓存成功");
        } catch (Exception e) {
            log.warn("清除所有看板缓存失败，error: {}", e.getMessage());
        }
    }

    // ==================== 缓存键生成工具 ====================

    /**
     * 生成看板缓存键
     */
    public String generateDashboardCacheKey(Long teacherId, Long classId, Long examId, 
                                           Boolean includeScoreStatistics, Boolean includeAdmissionAnalysis,
                                           Boolean includeStudentRanking, Boolean includeComparison) {
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(teacherId != null ? teacherId : "null");
        keyBuilder.append(":");
        keyBuilder.append(classId != null ? classId : "null");
        keyBuilder.append(":");
        keyBuilder.append(examId != null ? examId : "null");
        keyBuilder.append(":");
        keyBuilder.append(Boolean.TRUE.equals(includeScoreStatistics) ? "1" : "0");
        keyBuilder.append(Boolean.TRUE.equals(includeAdmissionAnalysis) ? "1" : "0");
        keyBuilder.append(Boolean.TRUE.equals(includeStudentRanking) ? "1" : "0");
        keyBuilder.append(Boolean.TRUE.equals(includeComparison) ? "1" : "0");
        
        return keyBuilder.toString();
    }

    // ==================== 缓存统计信息 ====================

    /**
     * 获取缓存统计信息
     */
    public Map<String, Object> getCacheStatistics() {
        try {
            // 统计各类缓存的数量
            Long examStatsCount = (long) redisTemplate.keys(EXAM_STATS_KEY + "*").size();
            Long classRankCount = (long) redisTemplate.keys(CLASS_RANK_KEY + "*").size();
            Long gradeStatsCount = (long) redisTemplate.keys(GRADE_STATS_KEY + "*").size();
            Long studentRankingCount = (long) redisTemplate.keys(STUDENT_RANKING_KEY + "*").size();
            Long subjectStatsCount = (long) redisTemplate.keys(SUBJECT_STATS_KEY + "*").size();
            Long dashboardCount = (long) redisTemplate.keys(DASHBOARD_KEY + "*").size();
            
            return Map.of(
                "examStatsCount", examStatsCount,
                "classRankCount", classRankCount,
                "gradeStatsCount", gradeStatsCount,
                "studentRankingCount", studentRankingCount,
                "subjectStatsCount", subjectStatsCount,
                "dashboardCount", dashboardCount,
                "totalCount", examStatsCount + classRankCount + gradeStatsCount + 
                             studentRankingCount + subjectStatsCount + dashboardCount,
                "updateTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
            );
            
        } catch (Exception e) {
            log.warn("获取缓存统计信息失败，error: {}", e.getMessage());
            return Map.of("error", e.getMessage());
        }
    }
}